import axios from "@/library/axios";
import cloneDeep from "lodash/cloneDeep";
import { processServerAPIData } from "@/library/utils/tools";
import { getToken, delToken } from "@/library/utils";
import MessageUti from "@/library/utils/MessageUti";
import useLanguage from "@/hooks/useLanguage";
import { RequestConfigEnum, RequestMethodEnum, ResultEnum } from "@/enums/httpEnum"
import Axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios";

// 响应数据类型
interface ResponseType<T> {
    Code: number
    Data: T
    Desc: string
    Type: number
}

class HttpRequestError {
    // 请求失败
    static loadingError(errorInfo: string) {
        MessageUti.showError(errorInfo);
    }

    // token 失效
    static async tokenError() {
        const confirm = await MessageUti.confirm("登录信息失效", "登录信息已失效，请重新登录", "", {
            showCancelButton: false,
            showClose: false,
            closeOnClickModal: false,
            closeOnPressEscape: false,
        })
        if (!confirm) { return }

        // 清除 token 并跳转登录页
        delToken();
        window.location.reload();
    }
}

class HttpRequestManager {
    static _PendingRequestMap = new Map()

    /**
     * 管理请求，处理重复请求，保持请求唯一
     * @param api 请求 url
     * @returns signal、DelMap 对象
     * @signal Axios配置 — signal
     * @RemoveRequest API请求结束后 — 移出队列方法
     */
    static _ManageUniqueRequest = (api: string) => {
        const controller = new AbortController()
        const signal = controller.signal

        // 终止队列中未完成请求
        if (this._PendingRequestMap.has(api)) { AbortApi(api) }
        this._PendingRequestMap.set(api, controller) // 请求进入队列

        // API从队列移除
        const RemoveRequest = () => { this._PendingRequestMap.delete(api) }

        return { signal, RemoveRequest }
    }

    /**
     * 终止指定API请求
     * @param api 要终止的API，可单独或数组集合传入
     * @example
     * 使用方式 AbortApi("xxx") 或 AbortApi(["xxx", "yyy"])
     */
    static AbortApi = (api: string | string[]) => {
        if (Array.isArray(api)) {
            // api 为数组时 遍历调用自身
            return api.forEach((api) => { AbortApi(api) })
        }

        const UnRequestApi: AbortController | undefined = this._PendingRequestMap.get(api)
        if (UnRequestApi) {
            // 终止这个未完成请求
            UnRequestApi.abort()
            // 移出队列
            this._PendingRequestMap.delete(api)
        }
    }

    /**
     * http 请求
     * @param api 请求 url
     * @param method 请求方式
     * @param requestParams 请求参数
     * @param config 请求配置（超时时间、请求头）
     * @param config.timeout 超时时间, 单位毫秒, 默认 0
     * @param config.headers 请求头
     * @returns Promise
     */
    static httpRequest<T = any>(
        api: string,
        method: RequestMethodEnum,
        requestParams = {},
        config: { timeout?: number; headers?: Record<string, any> } = {}
    ): Promise<ResponseType<T>> {
        const _token = getToken();
        const _params = cloneDeep(requestParams);
        processServerAPIData(_params);

        // 管理当前API请求
        const { signal, RemoveRequest } = this._ManageUniqueRequest(api)

        // axios 请求配置
        const RequestConfig: AxiosRequestConfig = {
            url: api,
            method,
            timeout: config.timeout || 0,
            headers: {
                ...config.headers,
                "X-API-TOKEN": _token.token,
                "X-Lang": useLanguage(),
            },
            data: (method === RequestMethodEnum.POST) ? _params : undefined,
            params: (method === RequestMethodEnum.GET) ? _params : undefined,
            signal  // 取消请求配置
        }

        return new Promise((resolve, reject) => {
            axios.request(RequestConfig)
                .then(({ data }: AxiosResponse<ResponseType<T>>) => {
                    const { Code, Type, Desc } = data;

                    if (Code !== ResultEnum.SUCCESS) {
                        if (Type === ResultEnum.TOKEN) { HttpRequestError.tokenError(); }
                        else {
                            HttpRequestError.loadingError(Desc);
                            reject(data)
                        }
                    } else { resolve(data); }

                    RemoveRequest()
                })
                .catch((error: AxiosError) => {
                    if (Axios.isCancel(error)) {
                        // 终止请求, 不再接收服务器响应数据
                    } else {
                        HttpRequestError.loadingError("请求出错或超时！请稍后重试");
                        RemoveRequest()
                    }
                });
        });
    }
}

/**
 * POST 请求
 * @param api 请求 url
 * @param data 请求 data 参数
 * @param config 请求配置（超时时间、请求头）
 * @param config.timeout 超时时间, 单位毫秒, 默认30000ms (30秒)
 * @param config.headers 请求头
 * @returns Promise
 */
const Post = <T = any>(
    api: string,
    data = {},
    config: { timeout?: number; headers?: Record<string, any> } = {}
): Promise<ResponseType<T>> => {
    return HttpRequestManager.httpRequest(api, RequestMethodEnum.POST, data, { timeout: RequestConfigEnum.TIMEOUT, ...config })
};

/**
 * GET 请求
 * @param api 请求 url
 * @param params 请求 params 参数
 * @param config 请求配置（超时时间、请求头）
 * @param config.timeout 超时时间, 单位毫秒, 默认30000ms (30秒)
 * @param config.headers 请求头
 * @returns Promise
 */
const Get = <T = any>(
    api: string,
    params = {},
    config: { timeout?: number; headers?: Record<string, any> } = {}
): Promise<ResponseType<T>> => {
    return HttpRequestManager.httpRequest(api, RequestMethodEnum.GET, params, { timeout: RequestConfigEnum.TIMEOUT, ...config })
};

/**
 * 终止指定API请求
 * @param api 要终止的API，可单独或数组集合传入
 * @example
 * 使用方式 AbortApi("xxx") 或 AbortApi(["xxx", "yyy"])
 */
const AbortApi = (api: string | string[]) => {
    HttpRequestManager.AbortApi(api)
}

export {
    Post,
    Get,
    AbortApi
};
